package com.bolt.admin.config;


import com.bolt.admin.properties.LoginProperties;
import com.bolt.admin.security.shiro.filter.APISubjectFactory;
import com.bolt.admin.security.shiro.filter.JWTFilter;
import com.bolt.admin.security.shiro.matcher.JwtMatcher;
import com.bolt.admin.security.shiro.realm.JWTTokenRealm;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
import org.apache.shiro.mgt.DefaultSubjectDAO;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.session.mgt.DefaultSessionManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.config.MethodInvokingFactoryBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;
import java.util.*;

/**
 * Created by Administrator on 2018/11/14.
 */
@Configuration
@ConditionalOnProperty(prefix = "login.jwt", name = {"enabled"}, havingValue = "true", matchIfMissing = false)
@EnableConfigurationProperties(LoginProperties.class)
public class ShiroJWTConfig {


    @Bean
    public LoginProperties loginProperties() {
        LoginProperties loginProperties = new LoginProperties();
        return loginProperties;
    }
    /**
     * 会话管理器
     *
     * @return
     */
    public DefaultSessionManager sessionManager() {
        DefaultSessionManager sessionManager = new DefaultSessionManager();
        // 关闭session定时检查，通过setSessionValidationSchedulerEnabled禁用掉会话调度器
        sessionManager.setSessionValidationSchedulerEnabled(false);
        return sessionManager;
    }


    /**
     * 安全管理器
     */

    @Bean
    public SecurityManager securityManager() {

        DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
        ModularRealmAuthenticator authenticator = new ModularRealmAuthenticator();
        DefaultSubjectDAO subjectDAO = (DefaultSubjectDAO) manager.getSubjectDAO();
        DefaultSessionStorageEvaluator evaluator = (DefaultSessionStorageEvaluator) subjectDAO.getSessionStorageEvaluator();
        APISubjectFactory subjectFactory = new APISubjectFactory(evaluator);
        manager.setSubjectFactory(subjectFactory);
        manager.setSessionManager(sessionManager());
        manager.setAuthenticator(authenticator);

        JWTTokenRealm jwtTokenRealm = new JWTTokenRealm();
        jwtTokenRealm.setCredentialsMatcher(new JwtMatcher());

        List <Realm> realms = new ArrayList <>();
        realms.add(jwtTokenRealm);
        manager.setRealms(realms);
        SecurityUtils.setSecurityManager(manager);
        return manager;

    }


    public JWTFilter jwtFilter() {
        JWTFilter jwtFilter = new JWTFilter();
        return jwtFilter;
    }


    @Bean
    public ShiroFilterFactoryBean shiroFilter() {
        SecurityManager securityManager = securityManager();
        ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
        shiroFilter.setSecurityManager(securityManager);

        HashMap <String, Filter> myFilters = new HashMap <>();
        myFilters.put("authc", jwtFilter());
        shiroFilter.setFilters(myFilters);

        /**
         * 配置shiro拦截器链
         *
         * anon  不需要认证
         * authc 需要认证*
         */
        Map <String, String> hashMap = new LinkedHashMap <>();
        hashMap.put("/assets/**", "anon");
        hashMap.put("/common/**", "anon");


        hashMap.put("/test", "anon");
        hashMap.put(" /swagger-resources", "anon");
        hashMap.put("/v2/api-docs", "anon");
        hashMap.put("/v2/api-docs-ext", "anon");
        hashMap.put("/doc.html", "anon");
        hashMap.put("/webjars/**", "anon");
        hashMap.put("/druid/**", "anon");
        hashMap.put("/ureport/**", "anon");
        hashMap.put("/auth/login", "anon");
        hashMap.put("/auth/logout", "anon");
        hashMap.put("/auth/captcha/graph", "anon");

        hashMap.put("/api/**", "authc");
        shiroFilter.setFilterChainDefinitionMap(hashMap);

        return shiroFilter;
    }

    /**
     * 在方法中 注入 securityManager,进行代理控制
     */
    @Bean
    public MethodInvokingFactoryBean methodInvokingFactoryBean(DefaultWebSecurityManager securityManager) {
        MethodInvokingFactoryBean bean = new MethodInvokingFactoryBean();
        bean.setStaticMethod("org.apache.shiro.SecurityUtils.setSecurityManager");
        bean.setArguments(new Object[]{securityManager});
        return bean;
    }

    /**
     * Shiro生命周期处理器:
     * 用于在实现了Initializable接口的Shiro bean初始化时调用Initializable接口回调(例如:UserRealm)
     * 在实现了Destroyable接口的Shiro bean销毁时调用 Destroyable接口回调(例如:DefaultSecurityManager)
     */
    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    /**
     * 启用shrio授权注解拦截方式，AOP式方法级权限检查
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor =
                new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }


}
